package cn.lblbc.ziptool

import android.content.Context
import android.net.Uri
import android.util.Log
import org.apache.commons.compress.archivers.ArchiveEntry
import org.apache.commons.compress.archivers.ArchiveInputStream
import org.apache.commons.compress.archivers.sevenz.SevenZFile
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream
import java.io.*
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream

object ArchiveUtils {
    private const val TAG = "ArchiveUtils"
    private const val BUFFER_SIZE = 8192

    fun getArchiveEntries(context: Context, uri: Uri, prefix: String = ""): List<ArchiveFileItem> {
        val fileItems = mutableListOf<ArchiveFileItem>()
        val fileExtension = getFileExtension(context, uri)
        
        try {
            when {
                fileExtension.equals("zip", ignoreCase = true) || 
                fileExtension.equals("apk", ignoreCase = true) || 
                fileExtension.equals("xapk", ignoreCase = true) -> {
                    readZipEntries(context, uri, fileItems, prefix)
                }
                fileExtension.equals("rar", ignoreCase = true) -> {
                    // 使用第三方库处理RAR文件
                    readRarEntries(context, uri, fileItems, prefix)
                }
                fileExtension.equals("7z", ignoreCase = true) -> {
                    read7zEntries(context, uri, fileItems, prefix)
                }
                fileExtension.equals("gz", ignoreCase = true) && getFileName(context, uri).endsWith(".tar.gz", ignoreCase = true) -> {
                    readTarGzEntries(context, uri, fileItems, prefix)
                }
                else -> {
                    Log.e(TAG, "Unsupported archive format: $fileExtension")
                }
            }
        } catch (e: Exception) {
            Log.e(TAG, "Error reading archive entries", e)
        }
        
        Log.d(TAG, "Total items found: ${fileItems.size} at prefix: $prefix")
        return fileItems
    }

    fun extractArchive(context: Context, uri: Uri, outputDir: File): Boolean {
        val fileExtension = getFileExtension(context, uri)
        
        return try {
            when {
                fileExtension.equals("zip", ignoreCase = true) || 
                fileExtension.equals("apk", ignoreCase = true) || 
                fileExtension.equals("xapk", ignoreCase = true) -> {
                    extractZip(context, uri, outputDir)
                }
                fileExtension.equals("rar", ignoreCase = true) -> {
                    extractRar(context, uri, outputDir)
                }
                fileExtension.equals("7z", ignoreCase = true) -> {
                    extract7z(context, uri, outputDir)
                }
                fileExtension.equals("gz", ignoreCase = true) && getFileName(context, uri).endsWith(".tar.gz", ignoreCase = true) -> {
                    extractTarGz(context, uri, outputDir)
                }
                else -> {
                    Log.e(TAG, "Unsupported archive format: $fileExtension")
                    false
                }
            }
        } catch (e: Exception) {
            Log.e(TAG, "Error extracting archive", e)
            false
        }
    }

    private fun getFileExtension(context: Context, uri: Uri): String {
        val fileName = getFileName(context, uri)
        val dotIndex = fileName.lastIndexOf('.')
        return if (dotIndex > 0) {
            fileName.substring(dotIndex + 1)
        } else {
            ""
        }
    }

    private fun getFileName(context: Context, uri: Uri): String {
        var result: String? = null
        context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
            if (cursor.moveToFirst()) {
                val nameIndex = cursor.getColumnIndex("_display_name")
                if (nameIndex >= 0) {
                    result = cursor.getString(nameIndex)
                }
            }
        }
        return result ?: "unknown"
    }

    // ZIP 文件处理 (包括 APK 和 XAPK)
    private fun readZipEntries(context: Context, uri: Uri, fileItems: MutableList<ArchiveFileItem>, prefix: String = "") {
        Log.d(TAG, "Reading ZIP entries with prefix: '$prefix'")
        val addedPaths = mutableMapOf<String, Boolean>() // path -> isDirectory
        context.contentResolver.openInputStream(uri)?.use { inputStream ->
            ZipInputStream(BufferedInputStream(inputStream)).use { zipInputStream ->
                var entry: ZipEntry?
                while (zipInputStream.nextEntry.also { entry = it } != null) {
                    entry?.let {
                        var entryName = it.name
                        // 处理以 ./ 或 .\ 开头的路径，与其他格式保持一致
                        if (entryName.startsWith("./") || entryName.startsWith(".\\")) {
                            entryName = entryName.substring(2)
                        }
                        
                        val startsWithPrefix = entryName.startsWith(prefix)
                        val notEqualToPrefix = entryName != prefix

                        if (startsWithPrefix && notEqualToPrefix) {
                            val relativePath = entryName.substring(prefix.length)
                            val pathSegments = relativePath.split("/").filter { it.isNotEmpty() }
                            
                            // 只处理直接子项（第一层）
                            if (pathSegments.isNotEmpty()) {
                                val firstName = pathSegments[0]
                                val isDirectory = pathSegments.size > 1 || it.isDirectory || entryName.endsWith("/")
                                
                                // 对于单层文件，直接使用文件名
                                // 对于多层路径或目录，使用目录名并添加 "/"
                                val finalIsDirectory = isDirectory
                                val finalDisplayPath = if (finalIsDirectory && !firstName.endsWith("/")) "$firstName/" else firstName
                                val finalPath = if (finalIsDirectory) {
                                    if (prefix.isEmpty()) "$firstName/" else "$prefix$firstName/"
                                } else {
                                    if (prefix.isEmpty()) firstName else "$prefix$firstName"
                                }

                                // 检查是否已添加该项目
                                val existingIsDirectory = addedPaths[finalDisplayPath]
                                if (existingIsDirectory == null) {
                                    // 新项目，直接添加
                                    addedPaths[finalDisplayPath] = finalIsDirectory
                                    fileItems.add(ArchiveFileItem(finalDisplayPath, finalIsDirectory, finalPath, entryName))
                                } else if (!existingIsDirectory && finalIsDirectory) {
                                    // 已存在的项目是文件，但新项目是目录，则更新为目录
                                    addedPaths[finalDisplayPath] = finalIsDirectory
                                    // 找到已存在的项目并更新它
                                    val index = fileItems.indexOfFirst { item -> item.name == finalDisplayPath }
                                    if (index >= 0) {
                                        fileItems[index] = ArchiveFileItem(finalDisplayPath, finalIsDirectory, finalPath, entryName)
                                        Log.d(TAG, "Updated ZIP item to directory: $finalDisplayPath")
                                    }
                                }
                            }
                        }
                    }
                }
                Log.d(TAG, "Finished reading ZIP entries. Total items: ${fileItems.size}, prefix: '$prefix'")
            }
        }
    }

    private fun extractZip(context: Context, uri: Uri, outputDir: File): Boolean {
        if (!outputDir.exists()) {
            outputDir.mkdirs()
        }
        
        try {
            context.contentResolver.openInputStream(uri)?.use { inputStream ->
                ZipInputStream(BufferedInputStream(inputStream)).use { zipInputStream ->
                    var entry: ZipEntry?
                    val buffer = ByteArray(BUFFER_SIZE)
                    
                    while (zipInputStream.nextEntry.also { entry = it } != null) {
                        entry?.let {
                            val file = File(outputDir, it.name)
                            
                            if (it.isDirectory) {
                                file.mkdirs()
                            } else {
                                // 确保父目录存在
                                file.parentFile?.mkdirs()
                                
                                FileOutputStream(file).use { fos ->
                                    var len: Int
                                    while (zipInputStream.read(buffer).also { len = it } > 0) {
                                        fos.write(buffer, 0, len)
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return true
        } catch (e: Exception) {
            Log.e(TAG, "Error extracting ZIP", e)
            return false
        }
    }

    // RAR 文件处理
    private fun readRarEntries(context: Context, uri: Uri, fileItems: MutableList<ArchiveFileItem>, prefix: String = "") {
        // 注意：需要添加第三方库来处理RAR文件，如junrar
        // 这里是简化实现，实际应用中需要添加相应的依赖
        try {
            // 使用临时文件处理RAR
            val tempFile = File(context.cacheDir, "temp.rar")
            context.contentResolver.openInputStream(uri)?.use { input ->
                FileOutputStream(tempFile).use { output ->
                    input.copyTo(output)
                }
            }
            
            // 使用junrar库处理RAR文件
            // 这里是示例代码，实际需要添加junrar依赖
            // com.github.junrar:junrar:7.5.4
            /*
            Archive archive = new Archive(tempFile);
            for (FileHeader fileHeader : archive.getFileHeaders()) {
                String name = fileHeader.getFileNameString();
                boolean isDirectory = fileHeader.isDirectory();
                fileItems.add(new ArchiveFileItem(name, isDirectory, name));
            }
            */
            
            // 临时添加一些示例项目
            fileItems.add(ArchiveFileItem("rar_example_file.txt", false, "rar_example_file.txt"))
            fileItems.add(ArchiveFileItem("rar_example_folder/", true, "rar_example_folder/"))
            
            tempFile.delete()
        } catch (e: Exception) {
            Log.e(TAG, "Error reading RAR entries", e)
        }
    }

    private fun extractRar(context: Context, uri: Uri, outputDir: File): Boolean {
        // 注意：需要添加第三方库来处理RAR文件，如junrar
        if (!outputDir.exists()) {
            outputDir.mkdirs()
        }
        
        try {
            // 使用临时文件处理RAR
            val tempFile = File(context.cacheDir, "temp.rar")
            context.contentResolver.openInputStream(uri)?.use { input ->
                FileOutputStream(tempFile).use { output ->
                    input.copyTo(output)
                }
            }
            
            // 使用junrar库处理RAR文件
            // 这里是示例代码，实际需要添加junrar依赖
            /*
            Archive archive = new Archive(tempFile);
            for (FileHeader fileHeader : archive.getFileHeaders()) {
                String name = fileHeader.getFileNameString();
                File file = new File(outputDir, name);
                
                if (fileHeader.isDirectory()) {
                    file.mkdirs();
                } else {
                    // 确保父目录存在
                    file.getParentFile().mkdirs();
                    
                    FileOutputStream fos = new FileOutputStream(file);
                    archive.extractFile(fileHeader, fos);
                    fos.close();
                }
            }
            */
            
            tempFile.delete()
            return true
        } catch (e: Exception) {
            Log.e(TAG, "Error extracting RAR", e)
            return false
        }
    }

    // 7Z 文件处理
    private fun read7zEntries(context: Context, uri: Uri, fileItems: MutableList<ArchiveFileItem>, prefix: String = "") {
        val addedPaths = mutableMapOf<String, Boolean>() // path -> isDirectory
        try {
            // 使用临时文件处理7z
            val tempFile = File(context.cacheDir, "temp.7z")
            context.contentResolver.openInputStream(uri)?.use { input ->
                FileOutputStream(tempFile).use { output ->
                    input.copyTo(output)
                }
            }
            
            SevenZFile(tempFile).use { sevenZFile ->
                var entry = sevenZFile.nextEntry
                while (entry != null) {
                    val name = entry.name
                    if (name.startsWith(prefix) && name != prefix) {
                        val relativePath = name.substring(prefix.length)
                        val pathSegments = relativePath.split("/").filter { it.isNotEmpty() }
                        
                        // 只处理直接子项（第一层）
                        if (pathSegments.isNotEmpty()) {
                            val firstName = pathSegments[0]
                            val isDirectory = entry.isDirectory && pathSegments.size == 1
                            val displayPath = if (isDirectory) "$firstName/" else firstName
                            
                            // 如果路径中有多个段，但第一个段是目录，则将其视为目录
                            val isSingleLevelDirectory = pathSegments.size == 1 && entry.isDirectory
                            val isMultiLevelEntry = pathSegments.size > 1
                            
                            // 对于多级路径，将第一级目录加入列表（如果尚未添加）
                            val finalIsDirectory = isSingleLevelDirectory || isMultiLevelEntry
                            val finalDisplayPath = if (isMultiLevelEntry) "$firstName/" else displayPath
                            
                            Log.d(TAG, "Processing 7Z entry: name=$name, relativePath=$relativePath, firstName=$firstName, segments=${pathSegments.size}, isDirectory=$isDirectory, finalDisplayPath=$finalDisplayPath, finalIsDirectory=$finalIsDirectory")
                            
                            // 检查是否已添加该项目
                            val existingIsDirectory = addedPaths[finalDisplayPath]
                            if (existingIsDirectory == null) {
                                // 新项目，直接添加
                                addedPaths[finalDisplayPath] = finalIsDirectory
                                // 确保目录路径以 "/" 结尾
                                val itemPath = if (finalIsDirectory && !finalDisplayPath.endsWith("/")) "${finalDisplayPath}/" else finalDisplayPath
                                fileItems.add(ArchiveFileItem(finalDisplayPath, finalIsDirectory, itemPath, name))
                                Log.d(TAG, "Added 7Z item: $finalDisplayPath")
                            } else if (!existingIsDirectory && finalIsDirectory) {
                                // 已存在的项目是文件，但新项目是目录，则更新为目录
                                addedPaths[finalDisplayPath] = finalIsDirectory
                                // 找到已存在的项目并更新它
                                val index = fileItems.indexOfFirst { item -> item.path == finalDisplayPath }
                                if (index >= 0) {
                                    fileItems[index] = ArchiveFileItem(finalDisplayPath, finalIsDirectory, finalDisplayPath, name)
                                    Log.d(TAG, "Updated 7Z item to directory: $finalDisplayPath")
                                }
                            }
                        }
                    }
                    entry = sevenZFile.nextEntry
                }
            }
            
            tempFile.delete()
        } catch (e: Exception) {
            Log.e(TAG, "Error reading 7Z entries", e)
        }
    }

    private fun extract7z(context: Context, uri: Uri, outputDir: File): Boolean {
        if (!outputDir.exists()) {
            outputDir.mkdirs()
        }
        
        try {
            // 使用临时文件处理7z
            val tempFile = File(context.cacheDir, "temp.7z")
            context.contentResolver.openInputStream(uri)?.use { input ->
                FileOutputStream(tempFile).use { output ->
                    input.copyTo(output)
                }
            }
            
            SevenZFile(tempFile).use { sevenZFile ->
                val buffer = ByteArray(BUFFER_SIZE)
                var entry = sevenZFile.nextEntry
                
                while (entry != null) {
                    val file = File(outputDir, entry.name)
                    
                    if (entry.isDirectory) {
                        file.mkdirs()
                    } else {
                        // 确保父目录存在
                        file.parentFile?.mkdirs()
                        
                        FileOutputStream(file).use { fos ->
                            var len: Int
                            while (sevenZFile.read(buffer).also { len = it } > 0) {
                                fos.write(buffer, 0, len)
                            }
                        }
                    }
                    
                    entry = sevenZFile.nextEntry
                }
            }
            
            tempFile.delete()
            return true
        } catch (e: Exception) {
            Log.e(TAG, "Error extracting 7Z", e)
            return false
        }
    }

    // TAR.GZ 文件处理
    private fun readTarGzEntries(context: Context, uri: Uri, fileItems: MutableList<ArchiveFileItem>, prefix: String = "") {
        val addedPaths = mutableMapOf<String, Boolean>() // path -> isDirectory
        context.contentResolver.openInputStream(uri)?.use { inputStream ->
            GzipCompressorInputStream(BufferedInputStream(inputStream)).use { gzipInputStream ->
                TarArchiveInputStream(gzipInputStream).use { tarInputStream ->
                    var entry: ArchiveEntry?
                    while (tarInputStream.nextEntry.also { entry = it } != null) {
                        entry?.let {
                            var name = it.name
                            // 处理以 ./ 或 .\ 开头的路径，去除前缀以避免重复
                            if (name.startsWith("./") || name.startsWith(".\\")) {
                                name = name.substring(2)
                            }
                            
                            // 过滤掉隐藏文件和目录（任何部分以点开头的路径项）
                            val pathSegments = name.split("/").filter { it.isNotEmpty() }
                            val containsHiddenSegment = pathSegments.any { it.startsWith(".") }
                            
                            if (!containsHiddenSegment && name.startsWith(prefix) && name != prefix) {
                                val relativePath = name.substring(prefix.length)
                                val relativeSegments = relativePath.split("/").filter { it.isNotEmpty() }
                                
                                // 只处理直接子项（第一层）
                                if (relativeSegments.isNotEmpty()) {
                                    val firstName = relativeSegments[0]
                                    val isDirectory = it.isDirectory && relativeSegments.size == 1
                                    val displayPath = if (isDirectory) "$firstName/" else firstName
                                    
                                    // 如果路径中有多个段，但第一个段是目录，则将其视为目录
                                    val isSingleLevelDirectory = relativeSegments.size == 1 && it.isDirectory
                                    val isMultiLevelEntry = relativeSegments.size > 1
                                    
                                    // 对于多级路径，将第一级目录加入列表（如果尚未添加）
                                    val finalIsDirectory = isSingleLevelDirectory || isMultiLevelEntry
                                    val finalDisplayPath = if (isMultiLevelEntry) "$firstName/" else displayPath
                                    
                                    // 确保不会添加以点开头的文件或目录
                                    if (!finalDisplayPath.startsWith(".")) {
                                        Log.d(TAG, "Processing TAR.GZ entry: name=$name, relativePath=$relativePath, firstName=$firstName, segments=${relativeSegments.size}, isDirectory=$isDirectory, finalDisplayPath=$finalDisplayPath, finalIsDirectory=$finalIsDirectory")
                                        
                                        // 检查是否已添加该项目
                                        val existingIsDirectory = addedPaths[finalDisplayPath]
                                        if (existingIsDirectory == null) {
                                            // 新项目，直接添加
                                            addedPaths[finalDisplayPath] = finalIsDirectory
                                            fileItems.add(ArchiveFileItem(finalDisplayPath, finalIsDirectory, finalDisplayPath, name))
                                            Log.d(TAG, "Added TAR.GZ item: $finalDisplayPath")
                                        } else if (!existingIsDirectory && finalIsDirectory) {
                                            // 已存在的项目是文件，但新项目是目录，则更新为目录
                                            addedPaths[finalDisplayPath] = finalIsDirectory
                                            // 找到已存在的项目并更新它
                                            val index = fileItems.indexOfFirst { item -> item.path == finalDisplayPath }
                                            if (index >= 0) {
                                                fileItems[index] = ArchiveFileItem(finalDisplayPath, finalIsDirectory, finalDisplayPath, name)
                                                Log.d(TAG, "Updated TAR.GZ item to directory: $finalDisplayPath")
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private fun extractTarGz(context: Context, uri: Uri, outputDir: File): Boolean {
        if (!outputDir.exists()) {
            outputDir.mkdirs()
        }
        
        try {
            context.contentResolver.openInputStream(uri)?.use { inputStream ->
                GzipCompressorInputStream(BufferedInputStream(inputStream)).use { gzipInputStream ->
                    TarArchiveInputStream(gzipInputStream).use { tarInputStream ->
                        val buffer = ByteArray(BUFFER_SIZE)
                        var entry: ArchiveEntry?
                        
                        while (tarInputStream.nextEntry.also { entry = it } != null) {
                            entry?.let {
                                val file = File(outputDir, it.name)
                                
                                if (it.isDirectory) {
                                    file.mkdirs()
                                } else {
                                    // 确保父目录存在
                                    file.parentFile?.mkdirs()
                                    
                                    FileOutputStream(file).use { fos ->
                                        var len: Int
                                        while (tarInputStream.read(buffer).also { len = it } > 0) {
                                            fos.write(buffer, 0, len)
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return true
        } catch (e: Exception) {
            Log.e(TAG, "Error extracting TAR.GZ", e)
            return false
        }
    }
}